<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Expires" content="0">
    <script src="../../lib/jquery.js"></script>
    <script src="../../lib/ramda.js"></script>
    <script src="./thex.js"></script>
    <script src="./compiler.js"></script>
    <script src="./vm.js"></script>
    <title>Thex-Assembly Simulate</title>
<style>
    html, body{
        margin: 0;
        padding: 0;
    }
    body{
        width: 100vw;
        height: 100vh;
        text-overflow: ellipsis;
        white-space: nowrap;
        overflow: auto;
    }

    pre, ul, input,select{
        margin: 0;
        padding: 0;
    }
    input,select{
        background-color: transparent;
        border: none;
        outline: none;
    }
    a{
        color:black;
    }
    label, .label{
        cursor: default;
        -webkit-touch-callout: none;
        -webkit-user-select: none;
        -khtml-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
    }
    .border-box{
        box-sizing:border-box;
        -moz-box-sizing:border-box;
        -webkit-box-sizing:border-box;
    }

    ux-page{
        display: none;
        width: 100%;
        height: 100%;
    }
    #page{
        display: none;
        width:80vw;
        margin:0 auto;
        font-size:14pt;
    }

    #menu{
        width:15%;
        height:95vh;
        background-color: grey;
        float:left;
    }
    #menu .item, #menu .item-action{
        height:10vh;
        line-height: 10vh;
        font-size: 3vh;
        color:#BBBBBB;
        text-align: center;
        cursor: default;
    }
    #menu .item-action{
        background-color: #505050;
        color:white;
        font-weight: bold;
    }
    #menu .line{
        height:1px;
        background-color: #CCCCCC;
    }

    .right, #editor{
        width: 85%;
        height: 95vh;
        background-color: #505050;
        float:right;
    }
    .right a{
        color:white;
    }
    .right .title{
        line-height: 5vh;
        font-size: 5vh;
        font-weight: bold;
    }
    .right .part{
        border-top: 1px solid #888888;
        margin-top: 24pt;
    }
    .right .part .title{
        line-height: 3vh;
        font-size: 3vh;
    }
    .right p{
        margin-top: 16pt;
    }
    .right pre{
        margin-top: 24pt;
        white-space: pre-wrap;
        word-wrap: break-word;
    }
    .right ul{
        list-style: none;
        margin-left: 32pt;
    }
    .right ul li{
        margin-top: 3pt;
        margin-bottom: 3pt;
    }

    #editor #linenumber{
        width:6%;
        height:100%;
        line-height: 16pt;
        color:#CDCDCD;
        border-right: 1px solid #888888;
        padding-right:10pt;
        text-align: right;
        font-size:12pt;
        overflow: hidden;
        padding-top:6pt;
        float:left;
    }
    #editor #code, .right{
        color:floralwhite;
        font-family: "Courier New", monospace;
        font-size: 16pt;
        line-height: 16pt;
        overflow-x: visible;
        overflow-y: auto;
        border: none;
        resize:none;
        outline:none;
    }
    #editor #code{
        width:93%;
        height:100%;
        background: none;
        overflow-x: auto;
        padding:0;
        padding-top:6pt;
        float:right;
    }

    #result, #dump{
        display: none;
        background-color:black;
        color:white;
        font-family:monospace;
        padding:6pt;
    }

    #help, #manual, #examples, #about{
        display: none;
        padding:10pt;
        cursor:default;
    }

    #status{
        height:5vh;
        line-height: 5vh;
        background-color:#303030;
        color:white;
        font-family:monospace;
        font-size: 2vh;
    }
</style>
</head>
<body>
    <div id="page">
        <!-- Left Menu -->
        <div id="menu">
            <div class="item border-box" data-cmd="run pageto" data-pageto="#result" data-path="."></div>
            <div class="line"></div>
            <div class="item border-box" data-cmd="compile_as_text pageto" data-pageto="#dump" data-path="."></div>
            <div class="line"></div>
            <div class="item-action border-box" data-cmd="pageto" data-pageto="#editor" data-path="."></div>
            <div class="line"></div>
            <div class="item border-box" data-cmd="pageto" data-pageto="#help" data-path="Help.html"></div>
            <div class="line"></div>
            <div class="item border-box" data-cmd="pageto" data-pageto="#manual" data-path="Manual.html"></div>
            <div class="line"></div>
            <div class="item border-box" data-cmd="pageto" data-pageto="#examples" data-path="Examples.html"></div>
            <div class="line"></div>
            <div class="item border-box" data-cmd="pageto" data-pageto="#about" data-path="About.html">About</div>
            <div class="line"></div>
        </div>
        <!-- Right ContentBoxs -->
        <textarea id="result" class="border-box right" readonly></textarea>
        <textarea id="dump" class="border-box right" readonly></textarea>
        <div id="editor" class="border-box">
            <div id="linenumber" class="border-box"></div>
            <textarea id="code" autofocus="true" wrap="off"></textarea>
            <div class="line-clear"></div>
        </div>
        <div id="help" class="border-box right">加载中</div>
        <div id="manual" class="border-box right">加载中</div>
        <div id="examples" class="border-box right">加载中</div>
        <div id="about" class="border-box right">加载中</div>
        <div class="line-clear"></div>
        <!-- Bottom StatusBox -->
        <div id="status" class="border-box"><center>Copyright© 2019 Thexasm/Clonne</center></div>
    </div>
    <div id="bad-browser-tips">
        <center>你的浏览器太差劲<br/>请使用高版本Chrome/Firefox内核的浏览器</center>
        <center>Your Browser too Bad<br/>Use Browser of High-Version Chrome/Firefox Kernel Please.</center>
    </div>
    <ux-page id="building">
        <center>此网页仍在编辑中，暂时无法访问</center>
        <center>This page in programming status, these fuck code has attack to my heart, oh fuck.</center>
    </ux-page>
</body>
<script>
    const TEXT = {
        MENU_ITEMS:[
            ["#result", "运行", "(<u>`+R</u>)"],
            ["#dump", "文本", "(<u>`+D</u>)"],
            ["#editor", "代码", "(<u>`+E</u>)"],
            ["#help", "帮助", "(<u>`+Q</u>)"],
            ["#manual", "手册", "(<u>`+A</u>)"],
            ["#examples", "示例", "(<u>`+X</u>)"],
        ],
        COMPILE_BEGIN:
"开始编译",
        COMPILE_ERROR:
"编译时出现错误：{}",
        RUN_BEGIN:
"开始运行",
        RUN_ERROR:
"运行时出现错误：{}",
        RUN_DONE:
"Runtimes: {RT}ms\n\
Registers:\n\
\tIP:{IP}\tSP:{SP}\tA:{A}\tB:{B}\tC:{C}\tD:{D}\tE:{E}\tF:{F}\n\
Stack:\n\
\t{STACK}\n\
Standard Output:\n{STDOUT}",
    }

    async function Run(code, fmsg){
        try{
            fmsg("OnCompileBegin")
            const program = thexasm_compiler.Compile(code)
            try{
                fmsg("OnRunBegin")
                const time_begin = Date.now()
                const run_result = thexasm_vm.Run(program)
                return ["OnRunDone", {runtimes:(Date.now()-time_begin), ...run_result}]
            } catch(e) {
                return ["OnRunError", e]
            }
        } catch(e) {
            return ["OnCompileError", e]
        }
    }
    function PageResult(box, fGetCode, eventps){
        eventps.CommandJoin("run", ()=>{
            const fmsg = (...rest)=>{eventps.Command(...rest)}
            Run(fGetCode(), fmsg).then((r)=>{
                eventps.Command(...r)
            });
        })
        eventps.CommandJoin("OnCompileBegin", ()=>{
            box.val(TEXT.COMPILE_BEGIN)
        })
        eventps.CommandJoin("OnCompileError", (info)=>{
            box.val(TEXT.COMPILE_ERROR.replace("{}", info));
        })
        eventps.CommandJoin("OnRunBegin", ()=>{
            box.val(TEXT.RUN_BEGIN)
        })
        eventps.CommandJoin("OnRunError", (info)=>{
            box.val(TEXT.RUN_ERROR.replace("{}", info))
        })
        eventps.CommandJoin("OnRunDone", (ctx)=>{
            function FormatStack(stack){
                if (stack.length > 0){
                    return stack.join('\t')
                } else return "Nothing"
            }
            function FormatStdout(stdout){
                if (stdout.length > 0){
                    return `\t${stdout.join('\n\t')}`
                } else return "\tNothing"
            }
            const result = TEXT.RUN_DONE
                .replace("{RT}", ctx.runtimes)
                .replace("{IP}", ctx.regs[thexasm_compiler.REG.IP])
                .replace("{SP}", ctx.regs[thexasm_compiler.REG.SP])
                .replace("{A}", ctx.regs[thexasm_compiler.REG.A])
                .replace("{B}", ctx.regs[thexasm_compiler.REG.B])
                .replace("{C}", ctx.regs[thexasm_compiler.REG.C])
                .replace("{D}", ctx.regs[thexasm_compiler.REG.D])
                .replace("{E}", ctx.regs[thexasm_compiler.REG.E])
                .replace("{F}", ctx.regs[thexasm_compiler.REG.F])
                .replace("{STACK}", FormatStack(ctx.stack))
                .replace("{STDOUT}", FormatStdout(ctx.stdout))
            box.val(result)
        })
    }

    async function Dump(code, fmsg){
        try{
            fmsg("OnDumpBegin")
            const text = thexasm_compiler.CompileAsText(code)
            return ["OnDumpDone", text]
        } catch(e) {
            return ["OnDumpError", e]
        }
    }
    function PageDump(box, fGetCode, eventps){
        eventps.CommandJoin("compile_as_text", ()=>{
            const fmsg = (...rest)=>{eventps.Command(...rest)}
            Dump(fGetCode(), fmsg).then((r)=>{
                eventps.Command(...r)
            })
        })
        eventps.CommandJoin("OnDumpBegin", ()=>{
            box.val(TEXT.COMPILE_BEGIN)
        });
        eventps.CommandJoin("OnDumpError", (info)=>{
            box.val(TEXT.COMPILE_ERROR.replace("{}", info))
        })
        eventps.CommandJoin("OnDumpDone", (text)=>{
            box.val(text)
        })
    }

    function Menu(box, eventps){
        const items = box.children("[data-cmd]")
        let last_action = items.filter(".item-action")
        let last_page = $(last_action.attr("data-pageto"))
        eventps.CommandJoin("pageto", (id)=>{
            const mi = items.filter("[data-pageto='?']".replace('?',id))
            const page = $(id)
            const CanSwitch = ()=>!(mi.hasClass("item-action"))
            function SwitchMenuitem(){
                mi.removeClass("item")
                mi.addClass("item-action")
                last_action.removeClass("item-action")
                last_action.addClass("item")
                last_action = mi
            }
            function SwitchPage(){
                function Display(){
                    last_page.hide()
                    last_page = page
                    last_page.show()
                }
                function LoadOnDemand(){
                    const page_path = mi.attr("data-path")
                    if ("." != page_path){
                        page.load(`assets/thexasm/${page_path}`)
                        mi.attr("data-path", ".")
                    }
                }
                Display()
                LoadOnDemand()
            }
            function AfterEffect(){
                if ("#editor" == id){
                    page.children("#code").focus()
                }
            }
            if (CanSwitch()){
                SwitchMenuitem()
                SwitchPage()
                AfterEffect()
            }
        })
        function OnItem(){
            const mi = $(this);
            const cmds = mi.attr("data-cmd").split(' ')
            function Parse(cmd){
                if ("pageto" == cmd){
                    return [cmd, mi.attr("data-pageto")]
                }
                else return [cmd]
            }
            for (const cmd of cmds){
                eventps.Command(...Parse(cmd))
            }
        }
        items.click(OnItem).mouseover(OnItem)
    }

    function PageEditor(box_ln, box_code, eventps){
        function MakeLinenumbers(n){
            function Cons(str, l){
                if (l > n){
                    return str
                }
                return Cons(str + (l.toString() + "<br/>"), l+1)
            }
            return Cons("", 1)
        }
        function CalcLinenumbers(str){
            const Count = (i, n)=>{
                if (i < str.length){
                    if ('\n' == str[i]){
                        n += 1
                    }
                    return Count(i+1, n)
                }
                return n
            }
            const Fix = (n)=>n+1
            return Fix(Count(0, 0))
        }

        let ln_last_max = 0
        function LinenumbersRefresh(ln_max){
            if (ln_max > 0 && ln_max != ln_last_max){
                box_ln.html(MakeLinenumbers(ln_max))
                ln_last_max = ln_max
            }
        }

        function Cursor(){
            return box_code.get(0).selectionStart
        }
        function CursorEnd(){
            return box_code.get(0).selectionEnd
        }
        function Select(start,end=start){
            box_code.get(0).selectionStart = start
            box_code.get(0).selectionEnd = end
        }

        function Insert(pos,text){
            const buffer = box_code.val()
            if (pos <= buffer.length){
                const valpair = [buffer.substr(0, pos), buffer.substr(pos)]
                box_code.val(valpair.join(text))
            }
        }
        function InsertByCursor(text){
            const cursor = Cursor()
            Insert(cursor, text)
            Select(cursor + text.length)
        }
        function CalcLinehead(code,cursor){
            function Loop(i){
                if (i < 1){
                    return i
                }
                else if ('\n' == code[i]){
                    return i+1
                }
                return Loop(i-1)
            }
            return Loop(cursor)
        }
        const TAB = "    "
        function IndentPush(start,end){
            Insert(CalcLinehead(box_code.val(), start), TAB)
            Select(start + TAB.length)
        }
        function IndentPop(start,end){
            InsertByCursor("|")
        }
        function FixUserInput(code){
            let lines = []
            let linecur = ""
            const tab = ""
            for (let i in code){
                const c = code[i]
                switch (c){
                    case '\t':
                        linecur += TAB
                        break
                    case '\n':
                    case '\r':
                        lines.push(linecur);
                        linecur = ""
                        break
                    default:
                        if (c >= ' '){
                            linecur += c
                        }
                }
            }
            lines.push(linecur)
            return lines.join('\n')
        }

        box_code.on("keydown",(e)=>{
            if (9 == e.keyCode) {
                e.preventDefault()
                if (e.shiftKey){
                    IndentPop(Cursor(), CursorEnd())
                } else {
                    IndentPush(Cursor(), CursorEnd())
                }
            }
            return true
        })
        box_code.on("keypress",(e)=>{
            if (eventps.key_in_tilde) {
                return false
            }
            return true
        })
        box_code.on("keyup",(e)=>{
            if (eventps.key_in_tilde && eventps.key_only_tilde){
                InsertByCursor('`')
            }
            return true
        })
        box_code.on("scroll", ()=>{
            box_ln.scrollTop(box_code.scrollTop());
        })
        box_code.on("input", ()=>{
            box_code.val(FixUserInput(box_code.val()));
            LinenumbersRefresh(CalcLinenumbers(box_code.val()))
        })
        LinenumbersRefresh(1)
    }

    function PageHelp(box, eventps){
    }
    function PageManual(box, eventps){
    }
    function PageExamples(box, eventps){
    }
    function PageAbout(box, eventps){
    }

    function Keyevents(eventps){
        eventps["keydown"].push((e)=>{
            if ('`' == e.key){
                eventps.key_in_tilde = true
            }
            else if (eventps.key_in_tilde) {
                eventps.key_only_tilde = false
            }
        })
        eventps["keypress"].push((e)=>{
        })
        eventps["keyup"].push((e)=>{
            if ('`' == e.key){
                eventps.key_in_tilde = false
                eventps.key_only_tilde = true
            }
        })
        const MakeProcesser = (name)=>(e)=>{
            const ps = eventps[name]
            for (i in ps){
                if (ps[i](e)){
                    break
                }
            }
        }
        $(this).on("keydown", MakeProcesser("keydown"))
        $(this).on("keypress", MakeProcesser("keypress"))
        $(this).on("keyup", MakeProcesser("keyup"))
    }
    function ShortcutKeys(eventps){
        eventps["keydown"].push((e)=>{
            if (eventps.key_in_tilde){
                switch (e.key){
                    case 'r': case 'R':
                        eventps.Command("run")
                        eventps.Command("pageto","#result")
                        return true
                    case 'd': case 'D':
                        eventps.Command("compile_as_text")
                        eventps.Command("pageto","#dump")
                        return true
                    case 'e': case 'E':
                        eventps.Command("pageto","#editor")
                        return true
                    case 'q': case 'Q':
                        eventps.Command("pageto","#help")
                        return true
                    case 'a': case 'A':
                        eventps.Command("pageto","#manual")
                        return true
                    case 'x': case 'X':
                        eventps.Command("pageto","#examples")
                        return true
                }
            }
            return false
        })
    }

    function FixSomeHtml(root){
        // Phone Test
        if (root.width() < root.height()) {
            root.css("width", "100%")
            const right_box_font_size = "28pt"
            const linenumber_font_size = "20pt"
            const linenumber_width = "15%"
            const code_font_size = "32pt"
            const code_width = "84%"
            $(".right").css("font-size", right_box_font_size).css("line-height", right_box_font_size)
            $("#editor #linenumber").css("width", linenumber_width).css("font-size", linenumber_font_size).css("line-height", code_font_size)
            $("#editor #code").css("width", code_width).css("font-size", code_font_size).css("line-height", code_font_size)
            for (const label of TEXT.MENU_ITEMS){
                $(`[data-pageto='${label[0]}']`).html(label[1])
            }
        } else {
            for (const label of TEXT.MENU_ITEMS){
                $(`[data-pageto='${label[0]}']`).html(`${label[1]}${label[2]}`)
            }
        }
    }

    function Start(eventps){
        $("#bad-browser-tips").hide()
        // This page building...
        $("ux-page#building").show()
        return
        const page = $("#page")
        const box_code =  $("#editor #code")
        PageEditor($("#editor #linenumber"), box_code, eventps)
        const fGetCode = ()=>box_code.val()
        PageResult($("#result"), fGetCode, eventps)
        PageDump($("#dump"), fGetCode, eventps)
        PageHelp($("#help"), eventps)
        PageManual($("#manual"), eventps)
        PageExamples($("#examples"), eventps)
        PageAbout($("#about"), eventps)
        Menu($("#menu"), eventps)
        FixSomeHtml(page)
        page.show()
    }
    $(()=>{
        const eventps = {
            _commands:{},
            CommandJoin(name,f){
                if (f instanceof Function){
                    this._commands[name] = f
                }
            },
            Command(name, ...rest){
                const f = this._commands[name]
                if (f instanceof Function){
                    f(...rest)
                } else console.log(name, f)
            },
            key_in_tilde:false,
            key_only_tilde:true,
            "keydown":[],
            "keypress":[],
            "keyup":[]
        };
        Keyevents(eventps)
        ShortcutKeys(eventps)
        Start(eventps)
    })
</script>
</html>
